from datetime import timedelta
from fastapi import APIRouter, Depends, HTTPException, status, Security
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm, SecurityScopes
from sqlalchemy.orm import Session
from typing import Dict, Any
from pydantic import BaseModel, EmailStr, Field
import logging

from app.database.database import get_db
from app.services.auth_service import (
    create_user,
    authenticate_user,
    verify_token,
    create_tokens
)
from app.core.config import settings
from app.utils.response_utils import ResponseResult

# 获取日志记录器
logger = logging.getLogger("app.routers.auth")

router = APIRouter(
    prefix="/auth", 
    tags=["认证"],
    responses={
        401: {"description": "未认证"},
        403: {"description": "无权限访问"},
        404: {"description": "资源不存在"},
        500: {"description": "服务器内部错误"}
    }
)

# OAuth2 密码Bearer
oauth2_scheme = OAuth2PasswordBearer(
    tokenUrl="/auth/login-form",
    scheme_name="OAuth2PasswordBearer",
    description="JWT认证",
    auto_error=True
)

class UserCreate(BaseModel):
    """用户注册请求模型"""
    username: str = Field(..., description="用户名", example="zhangsan")
    email: EmailStr = Field(..., description="邮箱地址", example="user@example.com")
    password: str = Field(..., description="密码", example="password123")

class Token(BaseModel):
    """认证令牌响应模型"""
    access_token: str = Field(..., description="访问令牌")
    refresh_token: str = Field(..., description="刷新令牌")
    token_type: str = Field(..., description="令牌类型", example="bearer")
    user: Dict[str, Any]

class RefreshToken(BaseModel):
    """刷新令牌请求模型"""
    refresh_token: str = Field(..., description="刷新令牌")

class LoginRequest(BaseModel):
    """登录请求模型（JSON格式）"""
    username: str = Field(..., description="用户名", example="zhangsan")
    password: str = Field(..., description="密码", example="password123")

@router.post("/register", 
    response_model=Dict[str, Any],
    summary="用户注册",
    description="创建新用户账号。需要提供用户名、邮箱和密码。用户名必须唯一。",
    tags=["认证"]
)
async def register(user: UserCreate, db: Session = Depends(get_db)):
    """用户注册
    
    创建新用户账号，用户名和邮箱必须是唯一的。
    
    参数:
        - **user**: 包含用户名、邮箱和密码的用户注册信息
    
    返回:
        - **message**: 注册结果信息
        
    可能的错误:
        - 400: 用户名已存在
        - 400: 邮箱已被使用
        - 500: 服务器内部错误
    """
    try:
        logger.info(f"尝试注册用户: {user.username}")
        create_user(db, user.username, user.email, user.password)
        logger.info(f"用户注册成功: {user.username}")
        return ResponseResult.success(message="注册成功")
    except HTTPException as e:
        logger.warning(f"用户注册失败: {user.username}, 原因: {e.detail}")
        raise e
    except Exception as e:
        logger.error(f"用户注册异常: {user.username}, 异常: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=str(e)
        )

@router.post("/login", 
    response_model=Dict[str, Any],
    summary="用户登录",
    description="""
    使用用户名和密码进行身份验证，成功后返回访问令牌。
    
    **请求格式**：
    ```json
    {
        "username": "zhangsan",
        "password": "password123"
    }
    ```
    
    **返回格式**：
    ```json
    {
        "code": 200,
        "message": "登录成功",
        "data": {
            "access_token": "eyJhbGciOiJIUzI1...",
            "refresh_token": "eyJhbGciOiJIUzI1...",
            "token_type": "bearer",
            "user": {
                "id": 1,
                "username": "example",
                "email": "user@example.com"
            }
        }
    }
    ```
    """,
    tags=["认证"]
)
async def login(
    login_data: LoginRequest,
    db: Session = Depends(get_db)
):
    """用户登录接口
    
    使用用户名和密码进行身份验证，返回访问令牌和用户信息
    """
    logger.info(f"用户尝试登录: {login_data.username}")
    try:
        # 验证用户
        user = authenticate_user(db, login_data.username, login_data.password)
        if not user:
            logger.warning(f"登录失败: 用户名或密码错误 - {login_data.username}")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误"
            )
            
        # 创建令牌数据
        token_data = {
            "sub": user.username,
            "user_id": user.id,
            "email": user.email
        }
        
        # 生成令牌
        tokens = create_tokens(token_data)
        
        # 创建响应数据
        response_data = {
            "access_token": tokens["access_token"],
            "refresh_token": tokens["refresh_token"],
            "token_type": "bearer",
            "user": {
                "id": user.id,
                "username": user.username,
                "email": user.email
            }
        }
        
        logger.info(f"用户登录成功: username={user.username}, user_id={user.id}")
        return ResponseResult.success(data=response_data, message="登录成功")
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"登录过程中发生错误: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="登录过程中发生错误"
        )

# 保留form表单登录方式，用于兼容性
@router.post("/login-form", 
    response_model=Dict[str, Any],
    summary="表单登录（兼容）",
    description="使用表单提交方式登录，供旧版客户端使用",
    tags=["认证"]
)
async def login_form(
    form_data: OAuth2PasswordRequestForm = Depends(),
    db: Session = Depends(get_db)
):
    """表单登录接口（兼容旧版客户端）
    
    使用x-www-form-urlencoded格式提交登录请求
    """
    logger.info(f"用户尝试表单登录: {form_data.username}")
    try:
        # 验证用户
        user = authenticate_user(db, form_data.username, form_data.password)
        if not user:
            logger.warning(f"表单登录失败: 用户名或密码错误 - {form_data.username}")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误"
            )
            
        # 创建令牌数据
        token_data = {
            "sub": user.username,
            "user_id": user.id,
            "email": user.email
        }
        
        # 生成令牌
        tokens = create_tokens(token_data)
        
        # 创建响应数据
        response_data = {
            "access_token": tokens["access_token"],
            "refresh_token": tokens["refresh_token"],
            "token_type": "bearer",
            "user": {
                "id": user.id,
                "username": user.username,
                "email": user.email
            }
        }
        
        logger.info(f"用户表单登录成功: username={user.username}, user_id={user.id}")
        return ResponseResult.success(data=response_data, message="登录成功")
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"表单登录过程中发生错误: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="登录过程中发生错误"
        )

@router.post("/refresh", 
    response_model=Dict[str, Any],
    summary="刷新访问令牌",
    description="使用有效的刷新令牌获取新的访问令牌和刷新令牌。当访问令牌过期时，可使用此接口获取新令牌而无需重新登录。",
    tags=["认证"]
)
async def refresh_token(
    refresh_token: RefreshToken,
    db: Session = Depends(get_db)
):
    """刷新访问令牌
    
    使用有效的刷新令牌获取新的访问令牌和刷新令牌。
    
    参数:
        - **refresh_token**: 刷新令牌
    
    返回:
        - **access_token**: 新的访问令牌
        - **refresh_token**: 新的刷新令牌
        - **token_type**: 令牌类型，始终为"bearer"
        - **user**: 用户信息，包含id、username和email
        
    可能的错误:
        - 401: 无效的刷新令牌
        - 401: 令牌已过期
    """
    try:
        # 验证刷新令牌
        payload = verify_token(refresh_token.refresh_token, is_refresh_token=True)
        username = payload.get("sub")
        user_id = payload.get("user_id")
        email = payload.get("email")
        
        # 创建新的令牌
        token_data = {
            "sub": username,
            "user_id": user_id,
            "email": email
        }
        tokens = create_tokens(token_data)
        
        # 返回新的令牌和用户信息
        result = {
            **tokens,
            "user": {
                "id": user_id,
                "username": username,
                "email": email
            }
        }
        
        return ResponseResult.success(data=result, message="令牌刷新成功")
        
    except HTTPException as e:
        logger.warning(f"令牌刷新失败, 原因: {e.detail}")
        raise e 

@router.post("/oauth/token",
    summary="OAuth2 认证",
    description="用于 Swagger UI 的 OAuth2 认证接口",
    include_in_schema=False  # 在文档中隐藏此接口
)
async def oauth2_login(
    form_data: OAuth2PasswordRequestForm = Depends(),
    db: Session = Depends(get_db)
):
    """OAuth2 认证接口
    
    专门用于处理 Swagger UI 的 OAuth2 认证流程
    直接返回标准的 OAuth2 格式响应
    """
    try:
        user = authenticate_user(db, form_data.username, form_data.password)
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误",
                headers={"WWW-Authenticate": "Bearer"},
            )
        
        # 创建标准OAuth2响应
        token_data = {"sub": user.username, "user_id": user.id, "email": user.email}
        tokens = create_tokens(token_data)
        
        return {
            "access_token": tokens["access_token"],
            "token_type": "bearer",
            "expires_in": settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES * 60,
            "scope": form_data.scopes or "",
            "refresh_token": tokens["refresh_token"]
        }
    except Exception as e:
        logger.error(f"OAuth2认证过程中发生错误: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="认证过程中发生错误"
        )

# 添加JSON格式的OAuth2认证端点
@router.post("/oauth/token-json",
    summary="OAuth2 JSON认证",
    description="用于客户端的OAuth2 JSON认证接口",
    include_in_schema=False  # 在文档中隐藏此接口
)
async def oauth2_login_json(
    login_data: LoginRequest,
    db: Session = Depends(get_db)
):
    """OAuth2 JSON认证接口
    
    处理使用JSON格式提交的OAuth2认证请求
    返回标准的OAuth2格式响应
    """
    try:
        user = authenticate_user(db, login_data.username, login_data.password)
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误",
                headers={"WWW-Authenticate": "Bearer"},
            )
        
        # 创建标准OAuth2响应
        token_data = {"sub": user.username, "user_id": user.id, "email": user.email}
        tokens = create_tokens(token_data)
        
        return {
            "access_token": tokens["access_token"],
            "token_type": "bearer",
            "expires_in": settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES * 60,
            "scope": "",
            "refresh_token": tokens["refresh_token"]
        }
    except Exception as e:
        logger.error(f"OAuth2 JSON认证过程中发生错误: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="认证过程中发生错误"
        ) 